#pragma once
#include <string>
#include <SceneGraph/node.h>
#include <Display/camera_interface.h>

namespace PhysLeo {

/**
 * scene graph class, which manages all the objects which need to be rendered or animated. 
 * every node in this graph is a real object or inter-object operation like collision detection and reaction
 */
class SceneGraph {
public:
    /**
     * explicit constructor
     * @param[in] root  root node
     */
	explicit SceneGraph(std::shared_ptr<Node> root) :root_(std::move(root)) {}

    /**
    * delete default copy constructor
    */
    SceneGraph(const SceneGraph&) = delete;

    /**
    * delete default move constructor
    */
    SceneGraph(SceneGraph&&) = delete;

    /**
    * delete default copy assignment function
    */
    SceneGraph& operator=(const SceneGraph&) = delete;

    /**
    * delete default move assignment function
    */
    SceneGraph& operator=(SceneGraph&&) = delete;

    /**
    * default virtual destructor
    */
    ~SceneGraph() = default;

    /**
     * read scene graph from file, now has not been implemented yet.
     * @param[in] filename  filepath relative to executing directory
     */
	void readFromFile(const std::string& filename);

    /**
    * init scene graph's openGL resource
    */
    void initGlResourceGraph() const;

    /**
    * draw graph
    * @param[in] camera  a camera object which implements camera interface
    */
    void drawGraph(CameraInterface &camera) const;

    /**
    * init scene graph's simulation resource
    */
    void initSimulationResourceGraph() const;

    /**
    * scene graph's animation steps forward dt second
    * @param[in] dt  time for one frame
    */
    void runGraph(float dt) const;

    /**
    * transform scene graph's simulation data to render data
    */
    void transformDrawDataGraph() const;

    /**
    * update scene graph's render data to GL buffer
    */
    void updateDrawDataGraph() const;

protected:
    /**
    * init node's openGL resource
    * @param[in] node
    */
    static void initGlResourceNode(const std::shared_ptr<Node>& node);

    /**
    * draw node
    * @param[in] camera  a camera object which implements camera interface
    * @param[in] node
    */
    static void drawNode(CameraInterface &camera, const std::shared_ptr<Node>& node);

    /**
    * init node's simulation resource
    * @param[in] node
    */
    static void initSimulationResourceNode(const std::shared_ptr<Node>& node);

    /**
    * scene graph's animation steps forward dt second
    * @param[in] dt  time for one frame
    * @param[in] node
    */
    static void runNode(float dt, const std::shared_ptr<Node>& node);

    /**
    * transform node's simulation data to render data
    * @param[in] node
    */
    static void transformDrawDataNode(const std::shared_ptr<Node>& node);

    /**
    * update node's render data to GL buffer
    * @param[in] node
    */
    static void updateDrawDataNode(const std::shared_ptr<Node>& node);

    /**
     * root node of the scene graph
     */
    std::shared_ptr<Node> root_;
};

}